﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Routing;

namespace TypedMvcActionHelper
{
    public static class MvcActionExpressionExtractor
    {
        public static MvcActionExpressionExtractor<TController> For<TController>()
            where TController:ControllerBase
        {
            return new MvcActionExpressionExtractor<TController>();
        } 
    }
    public class MvcActionExpressionExtractor<TController> where TController:ControllerBase
    {
        private MethodCallExpression _methodCall;
        public string GetControllerName()
        {
            var controllerName = typeof(TController).Name;
            if (!controllerName.EndsWith("Controller"))
            {
                throw new AmbiguousMatchException("Only support mvc controller!");
            }

            return controllerName.Substring(0, controllerName.Length - "Controller".Length);
        }

        public string GetActionName(Expression<Func<TController, ActionResult>> expression)
        {
            _methodCall = GetMethodCallExpression(expression);

            return _methodCall.Method.Name;
        }

        public  RouteValueDictionary GetRouteValueDictionary(Expression<Func<TController, ActionResult>> expression)
        {
            if (_methodCall == null)
            {
                throw new ArgumentNullException("Invoke GetActionName firstly");
            }

            var routeValues = new RouteValueDictionary();
            var arguments = _methodCall.Arguments;
            var parameters = _methodCall.Method.GetParameters();
            var nameAndArgumentPairs = arguments.Select((a, i) => new { Argument = a, ParamenterName = parameters[i].Name });
            foreach (var pair in nameAndArgumentPairs)
            {
                var name = pair.ParamenterName;
                var value = ExpressionEvaluation.GetExpressionValue(pair.Argument, null, null);

                var values = GetValues(name, value);
                foreach (var valuePair in values)
                {
                    routeValues.Add(valuePair.Key, valuePair.Value);
                }
            }

            return routeValues;
        }

        private  Dictionary<string, object> GetValues(string name, object value)
        {
            var values = new Dictionary<string, object>();
            if (value == null)
            {
                return values;
            }
            var valueType = value.GetType();
            if (valueType.IsSimpleType())
            {
                values.Add(name, value);
            }
            else
            {
                var properties = PropertyInfoHelper.GetProperties(valueType);
                foreach (var property in properties)
                {
                    values.Add(property.Name, property.GetValue(value));
                }
            }

            return values;
        }

        private  MethodCallExpression GetMethodCallExpression(Expression<Func<TController, ActionResult>> expression)
        {
            var call = expression.Body as MethodCallExpression;
            if (call == null)
            {
                throw new ArgumentException($"you must call a method of {typeof(TController).Name}");
            }

            return call;
        }
    }

    public static class SimpleTypeExtension
    {
        public static bool IsSimpleType(this Type type)
        {
            if (type.IsValueType || type == typeof(string))
            {
                return true;
            }
            return false;
        }
    }
}